package com.example.demo.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.example.common.entity.ArticleInfo;
import com.example.common.entity.UserInfo;
import com.example.common.tools.AjaxResult;
import com.example.common.tools.AppVariable;
import com.example.common.tools.PasswordUtils;
import com.example.common.tools.UserSessionUtils;
import com.example.demo.service.ArticleService;
import com.example.demo.service.CommentService;
import com.example.demo.service.FollowService;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.example.demo.common.UpdateInfoUtils;
import com.example.demo.entity.FollowInfo;
import com.example.demo.entity.vo.UserinfoVO;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private ArticleService articleService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private UpdateInfoUtils updateInfoUtils;

    @Autowired
    private FollowService followService;

    @RequestMapping("/reg")
    public AjaxResult reg(UserInfo userInfo) throws IOException {
        //非法参数的校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数非法!");
        }
        //检验当前用户名是否存在(用户名不可以重复)
        if(userService.getUserByName(userInfo.getUsername()) != null) {
            return AjaxResult.fail(403, "当前用户名已存在，请重新设置!");
        }
        //密码加盐处理
        userInfo.setPassword(PasswordUtils.encrypt(userInfo.getPassword()));
        return AjaxResult.success(userService.reg(userInfo));
    }




    //线程锁
    private Object lock = new Object();
    @RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request, HttpServletResponse response, String username, String password) throws IOException, ParseException {
        //非空校验
        if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(403, "账号或密码错误，请稍后重试!");
        }
        UserInfo userInfo = userService.getUserByName(username);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "账号不存在!");
        }
        //安全校验：当用户输入密码错误 3 次执行冻结(禁止用户登录)
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        if(userService.getStateByName(username) == 3) {
            //记录该用户开始冻结时间(格式yyyy-MM-ddThh:mm:ss)
            userInfo.setCreatetime(LocalDateTime.now());
            //修改用户开始冻结时间
            userService.updateUserInfoById(userInfo);
            //使用线程池，避免频繁创建和销毁线程
            AppVariable.threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        try {
                            //state + 1
                            userService.updateStateByName(username, userInfo.getState() + 1);
                            lock.wait(AppVariable.FREEZE_TIME); //这里为了演示效果，时间设置为 10s
                            //解冻：修改 state 为 0
                            userService.updateStateByName(username, 0);
                            lock.notify();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            return AjaxResult.fail(403, "账号已被冻结，请 10s 后重试!");
        } else if(userService.getStateByName(username) > 3) {
            //账号错误三次以上，计算剩余时间
            //将格式话时间转化为时间戳计算
            String[] time = userInfo.getCreatetime().toString().split("T");
            Date date = simpleDateFormat.parse(time[0] + " " + time[1]);
            //计算相差秒数(以下计算都是 毫秒级别，因此最后需要除 1000 换算到秒)
            return AjaxResult.fail(403, "账号已被冻结，请 "+
                    ((date.getTime() +  AppVariable.FREEZE_TIME - System.currentTimeMillis()) / 1000) +"s 后重试");
        }
        //ps:这里注意要对加盐密码解密
        if(userInfo == null || !PasswordUtils.check(password, userInfo.getPassword())) {
            //用户名不存在或密码错误
            if(!PasswordUtils.check(password, userInfo.getPassword())) {
                //密码错误，该用户的 state 需要 +1
                userService.updateStateByName(username, userInfo.getState() + 1);
            }
            return AjaxResult.fail(403, "账号或密码错误，请稍后重试!");
        }
        //登录成功
        HttpSession session = request.getSession(true);
        session.setAttribute(AppVariable.USER_SESSION_KEY, userInfo);
        //安全校验，登录成功后将 state 状态改回 0
        userService.updateStateByName(username, 0);
        return AjaxResult.success(1);
    }

    @RequestMapping("/myid")
    public AjaxResult myId(HttpServletRequest request) {
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getPassword()) ||
        !StringUtils.hasLength(userInfo.getUsername())) {
            return AjaxResult.fail(403, "请登录!");
        }
        return AjaxResult.success(userInfo.getId());
    }

    @RequestMapping("/showinfo")
    public AjaxResult showInfo(HttpServletRequest request, Integer id) throws JsonProcessingException {
        //非空校验
        if(id == null) {
            return AjaxResult.fail(403, "参数错误1");
        }
        UserInfo userInfo = userService.getUserById(id);
        //根据用户 id 来查询用户的文章总数
        int artCount = userService.getArtCountByUid(userInfo.getId());
        //获取用户总赞数和用户总评论数
        int comments = 0;
        int likes = 0;
        List<ArticleInfo> articleInfoList = articleService.getAllArtInfoByUid(userInfo.getId());
        for(ArticleInfo articleInfo : articleInfoList) {
            likes += articleInfo.getLikes();
            comments += commentService.getCommentsByAid(articleInfo.getId());
        }

        //通过深拷贝进行赋值
        UserinfoVO userInfoVO = new UserinfoVO();
        BeanUtils.copyProperties(userInfo, userInfoVO);
        //修改信息
        userInfoVO.setArtCount(artCount);
        userInfoVO.setLikes(likes);
        userInfoVO.setComments(comments);
        //检验该用户是否被当前登录用户关注
        UserInfo curUserInfo = UserSessionUtils.getUser(request);
        if(curUserInfo == null || !StringUtils.hasLength(curUserInfo.getUsername()) ||
                !StringUtils.hasLength(curUserInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误2");
        }
        if(!curUserInfo.getId().equals(id)) {
            userInfoVO.setFollow(followService.isFollow(curUserInfo.getId(), id) != 0);
        }
        //粉丝数量
        userInfoVO.setFans(followService.getFansByUid(userInfo.getId()));
        return AjaxResult.success(userInfoVO);
    }

    @RequestMapping("/logout")
    public AjaxResult logout(HttpSession session) {
        //这里是直接拿到 session
        if(session != null || session.getAttribute(AppVariable.USER_SESSION_KEY) != null) {
            //用户信息校验正确，接下来开始删除会话
            session.removeAttribute(AppVariable.USER_SESSION_KEY);
            return AjaxResult.success(1);
        }
        //注销失败
        return AjaxResult.fail(403, "session 信息错误!");
    }

    @RequestMapping("/show-aut")
    public AjaxResult showAut(HttpServletRequest request, Integer uid) {
        if(uid == null || uid <= 0) {
            //无效参数(例如通过 postman 进行交互)
            return AjaxResult.fail(403, "参数错误");
        }
        //根据 id 查询作者信息
        UserInfo userInfo = userService.getUserById(uid);
        if(userInfo == null || userInfo.getId() <= 0) {
            //无效参数
            return AjaxResult.fail(403, "id错误");
        }
        //根据用户 id 来查询用户的文章总数
        int artCount = userService.getArtCountByUid(userInfo.getId());
        //获取用户总赞数和用户总评论数
        int comments = 0;
        int likes = 0;
        List<ArticleInfo> articleInfoList = articleService.getAllArtInfoByUid(userInfo.getId());
        for(ArticleInfo articleInfo : articleInfoList) {
            likes += articleInfo.getLikes();
            comments += commentService.getCommentsByAid(articleInfo.getId());
        }

        //通过深拷贝进行赋值
        UserinfoVO userInfoVO = new UserinfoVO();
        BeanUtils.copyProperties(userInfo, userInfoVO);
        //修改信息
        userInfoVO.setArtCount(artCount);
        userInfoVO.setLikes(likes);
        userInfoVO.setComments(comments);

        //检验该用户是否被当前登录用户关注
        //检验当前用户是否登录
        UserInfo curUserInfo = UserSessionUtils.getUser(request);
        if(curUserInfo == null || !StringUtils.hasLength(curUserInfo.getUsername()) ||
                !StringUtils.hasLength(curUserInfo.getPassword())) {
            // 当前用户未登录
            userInfoVO.setFollow(false);
            return AjaxResult.success(userInfoVO);
        }
        //只有当前登录用户与作者 id 不一致时才设置 关注按钮
        if(!curUserInfo.getId().equals(uid)) {
            userInfoVO.setFollow(followService.isFollow(curUserInfo.getId(), uid) != 0);
        }

        return AjaxResult.success(userInfoVO);
    }

    @RequestMapping("/login-logout")
    public AjaxResult loginOrLogout(HttpSession session) {
        if(session != null && session.getAttribute(AppVariable.USER_SESSION_KEY) != null) {
            //登录
            return AjaxResult.success(1);
        } else {
            //未登录
            return AjaxResult.success(0);
        }
    }

    /**
     * 修改用户头像
     * @param request
     * @param photo
     * @return
     */
    @RequestMapping("/subphoto")
    public AjaxResult updatePhoto(HttpServletRequest request,
                                  @RequestPart("myphoto") MultipartFile photo) {
        //非空校验
        if(photo == null) {
            return AjaxResult.fail(403, "图片错误!");
        }
        //要上传的文件名
        String originalFileName = photo.getOriginalFilename();

        //获取用户信息(1.删除旧头像需要原来的旧头像的路径 2.修改图片需要用户 id)
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        if(!userInfo.getPhoto().equals("img/default.jpg")) {
            //不是修改默认头像的话就将旧头像删除即可
            File file = new File(AppVariable.IMG_PATH_ABSOLUTE
                    + userInfo.getPhoto().split("/")[2]);
            file.delete();
        }

        //获取文件后缀
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
        //生成图片名称，使用 UUID 避免相同图片名冲突，加上图片后缀
        String photoName = UUID.randomUUID().toString() + suffix;

        //图片保存路径(绝对路径/相对路径)
        //这里为什么还要用相对路径，只用绝对路径不行么?
        //因为禁止浏览器访问本地磁盘绝对路径路径，因此最简单的办法就是配置一个绝对路径用来保存文件，一个相对路径提供给浏览器访问
        String photoPathAbsolute = AppVariable.IMG_PATH_ABSOLUTE + photoName; //绝对路径
        String photoPathRelative = AppVariable.IMG_PATH_RELATIVE + photoName; //相对路径
        //生成文件(绝对路径)
        File saveFile = new File(photoPathAbsolute);
        try {
            //将上传文件绝对路径保存到服务器文件系统
            photo.transferTo(saveFile);
            //保存图片相对路径到数据库中
            userService.updatePhotoById(userInfo.getId(), photoPathRelative);
            //修改评论
            userInfo.setPhoto(photoPathRelative);
            updateInfoUtils.updateSessionAndComment(request, userInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //将图片相对路径返回给前端
        return AjaxResult.success(photoPathRelative);
    }

    @RequestMapping("/info")
    public AjaxResult getUserInfo(HttpServletRequest request) {
        //获取用户信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        return AjaxResult.success(userInfo);
    }

    @RequestMapping("/update-nickname")
    public AjaxResult updateNickname(HttpServletRequest request, String nickname) {
        //获取用户信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) &&
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        userInfo.setNickname(nickname);
        //更新评论和 session 信息
        updateInfoUtils.updateSessionAndComment(request, userInfo);
        return AjaxResult.success(userService.updateUserInfoById(userInfo));
    }

    @RequestMapping("/check-password")
    public AjaxResult checkOldPassword(HttpServletRequest request, String password) {
        //非空校验
        if(!StringUtils.hasLength(password)) {
            return AjaxResult.fail(403, "密码不能为空!");
        }
        //获取用户会话，并非空校验
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //校验密码是否正确
        if(!PasswordUtils.check(password, userInfo.getPassword())) {
            return AjaxResult.fail(403, "密码错误!");
        }
        return AjaxResult.success(1);
    }

    @RequestMapping("/update-password")
    public AjaxResult updatePassword(HttpServletRequest request, String password) {
        //非空校验
        if(!StringUtils.hasLength(password)) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //获取用户信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误");
        }
        //修改密码(记得一定要加盐)
        userInfo.setPassword(PasswordUtils.encrypt(password));
        //更新 session 信息
        updateInfoUtils.updateSessionAndComment(request, userInfo);
        return AjaxResult.success(userService.updateUserInfoById(userInfo));
    }

    @RequestMapping("/update-realname")
    public AjaxResult updateRealname(HttpServletRequest request, String realname) {
        //非空校验
        if(!StringUtils.hasLength(realname)) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //获取用户会话信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //修改真实姓名信息
        userInfo.setRealname(realname);
        //修改 session 信息
        UserSessionUtils.updateSession(request, userInfo);
        return AjaxResult.success(userService.updateUserInfoById(userInfo));
    }

    @RequestMapping("/update-idcard")
    public AjaxResult updateIdcard(HttpServletRequest request, String idcard) {
        //非空校验
        if(!StringUtils.hasLength(idcard)) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //通过 session 获取用户信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //修改身份证信息
        userInfo.setIdcard(idcard);
        //修改 session 信息
        UserSessionUtils.updateSession(request, userInfo);
        return AjaxResult.success(userService.updateUserInfoById(userInfo));
    }

    @RequestMapping("/check-security")
    public AjaxResult checkSecurity(HttpServletRequest request, UserInfo userInfo) {
        //非空校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getRealname()) ||
        !StringUtils.hasLength(userInfo.getIdcard())) {
            return AjaxResult.fail(403, "参数错误1");
        }
        //根据用户名获取用户信息
        UserInfo curUserInfo = userService.getUserByName(userInfo.getUsername());
        if(curUserInfo == null || !StringUtils.hasLength(curUserInfo.getUsername()) ||
        !StringUtils.hasLength(curUserInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误2");
        }
        //检验用户之前是否有设置过密保，如果没有设置，就以当前信息为密保，进行设置
        if(!StringUtils.hasLength(curUserInfo.getRealname()) ||
                !StringUtils.hasLength(curUserInfo.getIdcard())) {
            //未设置密保，就直接根据用户 id 进行信息修改，
            userInfo.setId(curUserInfo.getId());
            //设置的密码要进行加密处理
            userInfo.setPassword(PasswordUtils.encrypt(userInfo.getPassword()));
            return AjaxResult.success(userService.updateUserInfoById(userInfo));
        } else {
            //设置过密保，首先检验密保信息是否正确
            if(curUserInfo.getRealname().equals(userInfo.getRealname()) &&
            curUserInfo.getIdcard().equals(userInfo.getIdcard())) {
                //密保正确，进行修改密码
                userInfo.setId(curUserInfo.getId());
                //设置的密码要进行加密处理
                userInfo.setPassword(PasswordUtils.encrypt(userInfo.getPassword()));
                return AjaxResult.success(userService.updateUserInfoById(userInfo));
            } else {
                //密保错误，返回错误信息
                return AjaxResult.fail(403, "密保输入错误，请稍后重试!");
            }
        }
    }

    @RequestMapping("/get-checkpassword")
    public AjaxResult getCheckPassword() {
        //定义图形验证码的长宽
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(90, 50);
//        写出验证码(本地)
        lineCaptcha.write(AppVariable.IMG_CHECK);
//        //todo: 写出验证码(云服务器)
//        lineCaptcha.write(AppVariable.IMG_CHECK);
        //将验证码code 和路径 返回给前端
        HashMap<String, String> map = new HashMap<>();
        map.put("path", AppVariable.IMG_PATH_RELATIVE + "line.png");
        map.put("val", lineCaptcha.getCode());
        return AjaxResult.success(map);
    }

    @RequestMapping("/update-gitee")
    public AjaxResult updateGitee(HttpServletRequest request, String gitee) {
        //非空校验
        if(!StringUtils.hasLength(gitee)) {
            return AjaxResult.fail(403, "参数错误1");
        }
        //获取用户信息
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getPassword()) ||
        !StringUtils.hasLength(userInfo.getUsername())) {
            return AjaxResult.fail(403, "参数错误2");
        }
        //修改 gitee
        userInfo.setGitee(gitee);
        return AjaxResult.success(userService.updateUserInfoById(userInfo));
    }

    /**
     * 粉丝列表
     * @param request
     * @param uid 该主页的用户 id
     * @return
     */
    @RequestMapping("/fanlist")
    public AjaxResult getFansList(HttpServletRequest request, Integer uid) {
        //非空校验
        if(uid == null) {
            return AjaxResult.fail(403, "参数错误!");
        }
        //当前登录用户信息
        UserInfo curUserInfo = UserSessionUtils.getUser(request);
        if(curUserInfo == null || !StringUtils.hasLength(curUserInfo.getUsername()) ||
                !StringUtils.hasLength(curUserInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误2");
        }
        //获取该 uid 用户的所有粉丝 id
        List<FollowInfo> followInfos = followService.getFanInfoAllByUid(uid);
        //粉丝列表
        List<UserinfoVO> fanInfoList = new ArrayList<>();
        for(FollowInfo followinfo : followInfos) {
            //每一个粉丝的具体数据
            UserInfo fanInfo = userService.getUserById(followinfo.getUid());
            //通过深拷贝进行赋值
            UserinfoVO fanInfoVO = new UserinfoVO();
            BeanUtils.copyProperties(fanInfo, fanInfoVO);
            //如果当前登录用户出现在了粉丝列表，则不可以出现关注按钮
            if(!curUserInfo.getId().equals(fanInfo.getId())) {
                //检验当前用户是否关注过该粉丝
                fanInfoVO.setFollow(followService.isFollow(curUserInfo.getId(), fanInfo.getId()) != 0);
            }
            //添加到粉丝列表
            fanInfoList.add(fanInfoVO);
        }
        return AjaxResult.success(fanInfoList);
    }

    @RequestMapping("/concernlist")
    public AjaxResult getConcernList(HttpServletRequest request, Integer uid) {
        //非空校验
        if(uid == null) {
            return AjaxResult.fail(403, "参数错误1");
        }
        //获取当前登录用户信息
        UserInfo curUserInfo = UserSessionUtils.getUser(request);
        if(curUserInfo == null || !StringUtils.hasLength(curUserInfo.getUsername()) ||
        !StringUtils.hasLength(curUserInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误2");
        }
        //获取关注信息列表
        List<FollowInfo> followInfos = followService.getConcernListByUid(uid);
        //关注用户列表
        List<UserinfoVO> concerList = new ArrayList<>();
        for(FollowInfo followInfo : followInfos) {
            //获取每一个关注的用户信息
            UserInfo concerInfo = userService.getUserById(followInfo.getFollow_id());
            //通过深拷贝进行赋值
            UserinfoVO concernVO = new UserinfoVO();
            BeanUtils.copyProperties(concerInfo, concernVO);
            //如果当前登录用户出现在了粉丝列表，则不可以出现关注按钮
            if(!curUserInfo.getId().equals(concerInfo.getId())) {
                //检验当前用户是否关注过该粉丝
                concernVO.setFollow(followService.isFollow(curUserInfo.getId(), concerInfo.getId()) != 0);
            }
            concerList.add(concernVO);
        }
        return AjaxResult.success(concerList);
    }




}
